// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package printer

import (
	"go/build/constraint"
	"sort"
	"text/tabwriter"
)

func (p *printer) fixGoBuildLines() {
	if len(p.goBuild)+len(p.plusBuild) == 0 {
		return
	}

	// 查找
	// 在最后一个空行之后，我们找到了一个非注释。
	// （我们将在评论栏后添加另一个空行。）
	// 当我们开始删除
	// 请注意，我们正在处理tabwriter输入，因此每个注释
	// 都以tabwriter开头和结尾。转义字节。
	// 和一些换行符已变成\f字节。
	insert := 0
	for pos := 0; ; {
		// 跳过行首的前导空格。
		blank := true
		for pos < len(p.output) && (p.output[pos] == ' ' || p.output[pos] == '\t') {
			pos++
		}
		// 跳过
		if pos+3 < len(p.output) && p.output[pos] == tabwriter.Escape && p.output[pos+1] == '/' && p.output[pos+2] == '/' {
			blank = false
			for pos < len(p.output) && !isNL(p.output[pos]) {
				pos++
			}
		}
		// 跳过行末的\n。
		if pos >= len(p.output) || !isNL(p.output[pos]) {
			break
		}
		pos++

		if blank {
			insert = pos
		}
	}

	// 如果有
	// 请使用该点。（文件的前面部分总是可以的。）
	if len(p.goBuild) > 0 && p.goBuild[0] < insert {
		insert = p.goBuild[0]
	} else if len(p.plusBuild) > 0 && p.plusBuild[0] < insert {
		insert = p.plusBuild[0]
	}

	var x constraint.Expr
	switch len(p.goBuild) {
	case 0:
		// 综合
		for _, pos := range p.plusBuild {
			y, err := constraint.Parse(p.commentTextAt(pos))
			if err != nil {
				x = nil
				break
			}
			if x == nil {
				x = y
			} else {
				x = &constraint.AndExpr{X: x, Y: y}
			}
		}
	case 1:
		// 解析
		x, _ = constraint.Parse(p.commentTextAt(p.goBuild[0]))
	}

	var block []byte
	if x == nil {
		// 没有有效的
		// 将所有行放在一起，但不要管它们。
		// 请注意，这些都已被删除。
		for _, pos := range p.goBuild {
			block = append(block, p.lineAt(pos)...)
		}
		for _, pos := range p.plusBuild {
			block = append(block, p.lineAt(pos)...)
		}
	} else {
		block = append(block, tabwriter.Escape)
		block = append(block, "// go:build“…）
		block = append(block, x.String()...)
		block = append(block, tabwriter.Escape, '\n')
		if len(p.plusBuild) > 0 {
			lines, err := constraint.PlusBuildLines(x)
			if err != nil {
				lines = []string{"// +build error:“+err。Error（）}
			}
			for _, line := range lines {
				block = append(block, tabwriter.Escape)
				block = append(block, line...)
				block = append(block, tabwriter.Escape, '\n')
			}
		}
	}
	block = append(block, '\n')

	// 构建要从剩余输出中删除的行的排序列表。
	toDelete := append(p.goBuild, p.plusBuild...)
	sort.Ints(toDelete)

	// 在插入点之后收集输出，删除行，插入到后面。
	var after []byte
	start := insert
	for _, end := range toDelete {
		if end < start {
			continue
		}
		after = appendLines(after, p.output[start:end])
		start = end + len(p.lineAt(end))
	}
	after = appendLines(after, p.output[start:])
	if n := len(after); n >= 2 && isNL(after[n-1]) && isNL(after[n-2]) {
		after = after[:n-1]
	}

	p.output = p.output[:insert]
	p.output = append(p.output, block...)
	p.output = append(p.output, after...)
}

// appendLines类似于append（x，y…）
// 但它避免了创建双空行，
// 这将不是gofmt标准输出。
// 它假定只追加整段行，而不是行片段。
func appendLines(x, y []byte) []byte {
	if len(y) > 0 && isNL(y[0]) && // y开始于空行
		(len(x) == 0 || len(x) >= 2 && isNL(x[len(x)-1]) && isNL(x[len(x)-2])) { // x为空或结束于空行
		y = y[1:] // 删除y的前导空行
	}
	return append(x, y...)
}

func (p *printer) lineAt(start int) []byte {
	pos := start
	for pos < len(p.output) && !isNL(p.output[pos]) {
		pos++
	}
	if pos < len(p.output) {
		pos++
	}
	return p.output[start:pos]
}

func (p *printer) commentTextAt(start int) string {
	if start < len(p.output) && p.output[start] == tabwriter.Escape {
		start++
	}
	pos := start
	for pos < len(p.output) && p.output[pos] != tabwriter.Escape && !isNL(p.output[pos]) {
		pos++
	}
	return string(p.output[start:pos])
}

func isNL(b byte) bool {
	return b == '\n' || b == '\f'
}
